"""
Copyright 2012 Wieser Dietmar, Margit Mutschlechner.

This file is part of Multimedia Social Networking Platform.

Multimedia Social Networking Platform is free software: 
you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Multimedia Social Networking Platform is distributed
in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Multimedia Social Networking Platform.  
If not, see <http://www.gnu.org/licenses/>.

"""

from msnp.service import network
from msnp.logger import Logger
from msnp.service.network import logic
from msnp.service.network.sending import Sender

def processFinishCrash(message):
    """This function processes a finish crash message.
    
    Called after the reconnection of the ring.
    Retrieves and sends the stored backups from peers between the old and the new predecessor of the successor of the crashed identity.
    
    Args:
        message: The finish crash message (RTFinishCrashMessage instance)
    """

    network.NetworkLock.acquireFingerLock()
    network.NetworkLock.acquireBackupLock()
    try:
        Logger.info("  -> RT FINISH_CRASH procedure")
        Logger.info("    -> successor: " + message.getSuccessor().getNameBundle())
        Logger.info("    -> predecessor: " + message.getPredecessor().getNameBundle())
        logic.FingerLogic.recalculateFingertableLeave(message.getPredecessor(), None, message.getSuccessor(), True)
        Logger.info("    -> fingertable recalculated")
        #search for backups and callback
        predecessorHashedName = message.getPredecessor().getHashedNameLong()
        oldPredecessorOfSuccessorHashedName = message.getOldPredecessorOfSuccessor().getHashedNameLong()
        backups = network.BackupController.selectBackupFromTo(predecessorHashedName, oldPredecessorOfSuccessorHashedName)
        if backups != None:
            #update backups with current timestamp of receiver
            timestamp = Sender.getTimestamp(message.getSuccessor())
            for backup in backups:
                backup.setTimestamp(timestamp)
            #callbacks have to go to the needed identity = successor
            Sender.backupCallback(message.getSuccessor(), backups)
            Logger.info("    -> backup sent to successor of crashed identity")
        else:
            Logger.info("    -> no backup to send to successor of crashed identity")
    finally:
        network.NetworkLock.releaseBackupLock()
        network.NetworkLock.releaseFingerLock()
    
    
def processLeave(message):
    """This function processes a leave message.
    
    Called after the leave of a super peer.
    Recalculate the own finger table.
    
    Args:
        message: The leave message (RTLeaveMessage instance)
    """
    
    network.NetworkLock.acquireFingerLock()
    network.NetworkLock.acquireBackupLock()
    try:
        Logger.info("  -> RT LEAVE procedure for peer " + message.getLeavedIdentity().getName() + " (between " + message.getPredecessor().getName() + " and " + message.getSuccessor().getName() + ")")
        #original=leaveidentity, needed=successor
        logic.FingerLogic.recalculateFingertableLeave(message.getPredecessor(), message.getLeavedIdentity(), message.getSuccessor())
        Logger.info("    -> fingertable recalculated")
    finally:
        network.NetworkLock.releaseBackupLock()
        network.NetworkLock.releaseFingerLock()
    
    
def processJoin(message):
    """This function processes a join message.
    
    Called after an upgrade of a new super peer.
    Recalculates the own fingertable and sends a callback if this peer is a candidate for certain fingers of the upgrading peer.
    
    Args:
        message: The join message (RTJoinMessage instance)
    """
    
    myIdentity = network.NetworkController.getMySelf().getMyIdentity()
    myName = myIdentity.getName()
    #the sender of the broadcast join message needs not to process the message, because it made a responsibility-search for the fingers
    if myName != message.getJoinIdentity().getName():
        network.NetworkLock.acquireFingerLock()
        network.NetworkLock.acquireBackupLock()
        try:
            Logger.info("  -> RT JOIN procedure for " + message.getJoinIdentity().getName())
            #original=joinidentity
            logic.FingerLogic.recalculateFingertableJoin(message.getJoinIdentity())
            Logger.info("    -> fingertable recalculated")
            [lowerIndex, upperIndex] = logic.FingerLogic.calculateFingerRangeImResponsibleForIdentity(message.getJoinIdentity())
            if lowerIndex > -1 and upperIndex > -1:
                Sender.callbackResponsibleIdentityForFinger(myIdentity, message.getJoinIdentity(), lowerIndex, upperIndex)
                Logger.info("    -> sent finger callback for range [" + str(lowerIndex) + ", " + str(upperIndex) + "]")
        finally:
            network.NetworkLock.releaseBackupLock()
            network.NetworkLock.releaseFingerLock()


def processSearchSuccessor(message):
    """This function processes a search successor message.
    
    Called after the recognition of a crash. Sequentially delivered messages are ignored.
    In the case of a broadcast the current peer sends a callback to the searching peer 
    containing the own identity and a boolean whether the crashed peer was its predecessor or not.
    
    Args:
        message: The search successor message (RTSearchSuccessorMessage instance)
    """
    
    if message.isUseBroadcast():
        Logger.info("  -> RT SEARCH_SUCCESSOR procedure for peer " + message.getCrashedIdentity().getName())
        crashedIdentity = message.getCrashedIdentity()
        predecessorOfCrashedIdentity = message.getPredecessorOfCrashedIdentity()
        predecessorHashedNameLong = network.NetworkController.getMySelf().getPredecessor().getHashedNameLong()
        successorOfCrashPeerFound = crashedIdentity.getHashedNameLong() == predecessorHashedNameLong
        Sender.callbackCrashBroadcast(crashedIdentity, predecessorOfCrashedIdentity, successorOfCrashPeerFound)
        Logger.info("    -> callback sent")
    else:
        Logger.info("  -> ignore SEARCH_SUCCESSOR procedure for peer " + message.getCrashedIdentity().getName() + " because it is send sequentially")


def processRehello(message):
    """This function processes a re-hello message.
    
    If this peer is inside the given range it re-authenticates by sending a new hello to a certain static peer,
    otherwise the message is ignored.
    
    Args:
        message: The re-hello message (RTRehelloMessage instance)
    """
    
    Logger.info("  -> RT REHELLO procedure between " + message.getFromIdentity().getName() + " and " + message.getToIdentity().getName())
    myHashNameLong = network.NetworkController.getMySelf().getMyIdentity().getHashedNameLong()
    fromHashNameLong = message.getFromIdentity().getHashedNameLong() - 1
    toHashNameLong = message.getToIdentity().getHashedNameLong() - 1
    #if i am located between needed and original identity
    if(logic.ResponsibilityLogic.isResponsible(fromHashNameLong, myHashNameLong, toHashNameLong)):
        Logger.info("    -> I have to rehello!!")
        Sender.hello(True, None)
    else:
        Logger.info("    -> I am out of range for rehello")
        
        
def processDeleteBackup(message):
    """This function processes a delete backup message.
    
    This function is called if a peer crashed, the ring got closed again and the backups are already collected.
    It deletes backups from the own database based on the given user names.
    
    Args:
        message: The delete backup message (RTDeleteBackupMessage instance)
    """
    
    Logger.info("  -> RT DELETE_BACKUP procedure, received from " + message.getSenderIdentity().getName())
    backupFromUsersToDelete = message.getBackupFromUsersToDelete()
    if backupFromUsersToDelete != None:
        for fromUserName in backupFromUsersToDelete:
            Logger.info("     -> delete backupFrom " + fromUserName)
            network.BackupController.deleteBackupForFromName(fromUserName)
